package org.javaongems.client.vcl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.javaongems.client.rt.awt.Rectangle;
import org.javaongems.client.rt.util.ArrayUtils;

public class Component extends Persistent {
	final static public int DESIGNING = 1;
	final static public int LOADING = 1 << 1;
	final static public int READING = 1 << 2;
	final static public int WRITING = 1 << 3;
	final static public int DESTROYING = 1 << 4;
	
	final static public String MULTI_ELEMENT_DELIM = ",";
	
	final static public String PARENT_PROP = "parent";
	final static public String CHILDREN_COUNT_PROP = "children";
	final static public String TOP_LEFT_PROP = "top-left";
	
	protected int state;
	protected Container owner;
	protected Component parent;
	protected String name;
	protected ArrayList children = new ArrayList();
	protected Rectangle boundsRect = new Rectangle();
	
	public String getNamePath() {
		return getName();
	}
	
	public String getName() {
		return name;
	}
	
	public String setName(String nm) {
		name = nm;
		return name;
	}
	
	public void setContainer(Container ownr) {
		owner = ownr;
	}
	
	public Persistent getOwner() {
		return owner;
	}

	public Component getParentComponent() {
		return parent;
	}
	
	public void setParentComponent(Component prnt) {
		parent = prnt;
		Container ownr = null;
		if (prnt != null) {
			ownr = (Container) prnt.getOwner();
			if (ownr == null) {
				Component p = prnt;
				while ((p = p.getParentComponent()) != null) {
					ownr = (Container) p.getOwner();
					if (ownr != null)
						break;
				}
				if (ownr == null)
					ownr = (Container) (p != null ? p: prnt);
			}
		}
		setContainer(ownr);
	}
	
	public boolean hasParent() {
		return parent != null;
	}
	
	protected void modifyStateMask(int mask, boolean on) {
		if (on)
			state = state | mask;
		else
			state = state & ~mask;
	}
	
	protected boolean hasMask(int mask) {
		return ((state & mask) != 0);
	}
	
	public boolean isDesigning() {
		return hasMask(DESIGNING);    
	}

	public boolean isLoading() {
		return hasMask(LOADING);    
	}
	
	public void setDesigning() {
		modifyStateMask(DESIGNING, true);
		for (Iterator iter = children.iterator(); iter.hasNext();) {
			Component child = (Component) iter.next();
			child.setDesigning();
		}
	}
	
	public int getComponentCount() {
		return children.size();
	}
	
	public List getComponents() {
		return children;
	}
	
	public boolean setComponentIndex(int index) {
		if (owner == null)
			return false;
		int curr = getComponentIndex();
		if (curr == index)
			return false;
		owner.children.remove(curr);
		owner.children.add(index, this);
		return true;
	}
	
	public int getComponentIndex() {
		if (owner == null)
			return -1;
		return owner.getComponents().indexOf(this);
	}
	
	public void destroy() {
		modifyStateMask(DESTROYING, true);
		try {
			for (Iterator iter = children.iterator(); iter.hasNext();) {
				Component child = (Component) iter.next();
				child.destroy();
			}
			children.clear();
			destroyed();
		} finally {
			modifyStateMask(DESTROYING, false);
		}
	}
	
	public Component findComponent(String name) {
		for (Iterator iter = children.iterator(); iter.hasNext();) {
			Component child = (Component) iter.next();
			if (child.getName().equals(name))
				return child;
		}
		return null;
	}
	
	public void insertComponent(Component child) {
		if (isDesigning())
			child.setDesigning();
		if (!children.contains(child)) {
			children.add(child);
			child.setParentComponent(this);
		}
	}
	
	public void removeComponent(Component child) {
		if (children.remove(child)) 
			child.setParentComponent(null);	
	}
	
	public int getTop() { return boundsRect.y; };
	public int getLeft() { return boundsRect.x; };
	public void setTop(int t) { boundsRect.y = t; };
	public void setLeft(int l) { boundsRect.x = l; };
	
	protected void destroyed() {
		// there are no children to notify they have all been removed
		// this is a the last chance to release resources
		owner = null;
		parent = null;
		children.clear(); children = null;
		boundsRect = null;
	}
	
	protected void loaded(ComponentReader rdr) {
		for (Iterator iter = children.iterator(); iter.hasNext();) {
			Component child = (Component) iter.next();
			child.loaded(rdr);
		}
		readDeferredReferences(rdr);
	}
	
	protected void readDeferredReferences(ComponentReader rdr) {
		// expect subclasses to write these		
	}
	
	public void readState(ComponentReader rdr) {
		modifyStateMask(READING, true);
		try {
			Container owner = rdr.getContainer();
			Component prnt = rdr.getParent();
			// first assign container & add self to the parent
			if (owner != this)
				setContainer(owner);
			if (prnt != this) 	
				prnt.insertComponent(this);
			String name = rdr.readString();
			setName(name);
			PropertiesReader pr = rdr.readProperties();
			int childCount = readCoreProperties(pr);
			pr = rdr.readProperties();
			readProperties(pr);
			for (int i = 0; i < childCount; i++) 
				rdr.readComponent();
		} finally {
			modifyStateMask(READING, false);
		}
	}
	
	public void writeState(ComponentWriter wrtr) {
		modifyStateMask(WRITING, true);
		try {
			wrtr.writeString(getName());
			PropertiesWriter pw = wrtr.writeProperties();
			writeCoreProperties(pw);
			pw = wrtr.writeProperties();
			writeProperties(pw);
			for (Iterator iter = children.iterator(); iter.hasNext();) {
				Component child = (Component) iter.next();
				wrtr.writeComponent(child);
			}
		} finally {
			modifyStateMask(WRITING, false);
		}
	}
	
	protected void writeCoreProperties(PropertiesWriter pw) {
		int childCount = getComponentCount();
		if (childCount > 0)
			pw.writeNumber(CHILDREN_COUNT_PROP, getComponentCount());
		writeTopLeftProperties(pw);
	}

	protected void writeTopLeftProperties(PropertiesWriter pw) {
		if (boundsRect.y != 0 || boundsRect.x != 0) 
			writeDelimitedProperties(new int[]{boundsRect.y, boundsRect.x}, TOP_LEFT_PROP, pw);
	}
	
	protected int readCoreProperties(PropertiesReader pr) {
		int childCount = (int) pr.readNumber(CHILDREN_COUNT_PROP, 0);
		readTopLeftAsString(pr);
		return childCount;
	}

	protected void readTopLeftAsString(PropertiesReader pr) {
		String topLeftAsStr = pr.readString(TOP_LEFT_PROP, null);
		if (topLeftAsStr != null) {
			int[] topLeft = ArrayUtils.parseIntArray(topLeftAsStr, ArrayUtils.COMMA_DELIM);
			setTop(topLeft[0]);
			setLeft(topLeft[1]);
		}
	}
	
	protected void writeProperties(PropertiesWriter wrtr) {
		// expect subclasses to write these
	}

	protected void readProperties(PropertiesReader rdr) {
		// expect subclasses to read these
	}
	
	protected void writeDelimitedProperties(String[] args, String prop, PropertiesWriter pw) {
		pw.writeString(prop, ArrayUtils.constructStringList(args, ArrayUtils.COMMA_DELIM));
	}

	protected void writeDelimitedProperties(int[] args, String prop, PropertiesWriter pw) {
		pw.writeString(prop, ArrayUtils.constructStringList(args, ArrayUtils.COMMA_DELIM));
	}
	
	protected void writeDelimitedProperties(double[] args, String prop, PropertiesWriter pw) {
		pw.writeString(prop, ArrayUtils.constructStringList(args, ArrayUtils.COMMA_DELIM));
	}

	protected void writeDelimitedProperties(boolean[] args, String prop, PropertiesWriter pw) {
		pw.writeString(prop, ArrayUtils.constructStringList(args, ArrayUtils.COMMA_DELIM));
	}
}
